﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;

namespace ReprojectWebMercator
{
    internal class getPolylineWindowCoordinatesECSample: CesiumGLSL
    {
        public vec4 getPolylineWindowCoordinates(vec4 position, vec4 previous, vec4 next, float expandDirection, float width, bool usePrevious, out float angle)
        {
            vec4 positionEC = czm_modelViewRelativeToEye * position;
            vec4 prevEC = czm_modelViewRelativeToEye * previous;
            vec4 nextEC = czm_modelViewRelativeToEye * next;
            return getPolylineWindowCoordinatesEC(positionEC, prevEC, nextEC, expandDirection, width, usePrevious, out angle);
        }
        public void clipLineSegmentToNearPlane(    vec3 p0,    vec3 p1,    out vec4 positionWC,    out bool clipped,    out bool culledByNearPlane,    out vec4 clippedPositionEC)
        {
            culledByNearPlane = false;
            clipped = false;

            vec3 p0ToP1 = p1 - p0;
            float magnitude = length(p0ToP1);
            vec3 direction = normalize(p0ToP1);

            // Distance that p0 is behind the near plane. Negative means p0 is
            // in front of the near plane.
            float endPoint0Distance = czm_currentFrustum.x + p0.z;

            // Camera looks down -Z.
            // When moving a point along +Z: LESS VISIBLE
            //   * Points in front of the camera move closer to the camera.
            //   * Points behind the camrea move farther away from the camera.
            // When moving a point along -Z: MORE VISIBLE
            //   * Points in front of the camera move farther away from the camera.
            //   * Points behind the camera move closer to the camera.

            // Positive denominator: -Z, becoming more visible
            // Negative denominator: +Z, becoming less visible
            // Nearly zero: parallel to near plane
            float denominator = -direction.z;

            if (endPoint0Distance > 0.0 && abs(denominator) < czm_epsilon7)
            {
                // p0 is behind the near plane and the line to p1 is nearly parallel to
                // the near plane, so cull the segment completely.
                culledByNearPlane = true;
            }
            else if (endPoint0Distance > 0.0)
            {
                // p0 is behind the near plane, and the line to p1 is moving distinctly
                // toward or away from it.

                // t = (-plane distance - dot(plane normal, ray origin)) / dot(plane normal, ray direction)
                float t = endPoint0Distance / denominator;
                if (t < 0.0 || t > magnitude)
                {
                    // Near plane intersection is not between the two points.
                    // We already confirmed p0 is behind the naer plane, so now
                    // we know the entire segment is behind it.
                    culledByNearPlane = true;
                }
                else
                {
                    // Segment crosses the near plane, update p0 to lie exactly on it.
                    p0 = p0 + t * direction;

                    // Numerical noise might put us a bit on the wrong side of the near plane.
                    // Don't let that happen.
                    p0.z = min(p0.z, -1*czm_currentFrustum.x);

                    clipped = true;
                } 
            }

            clippedPositionEC = vec4(p0, 1.0f);
            positionWC = czm_eyeToWindowCoordinates(clippedPositionEC);
        }

        public vec4 getPolylineWindowCoordinatesEC(vec4 positionEC, vec4 prevEC, vec4 nextEC, float expandDirection, float width, bool usePrevious, out float angle)
        {
            angle = 0f;

            // expandDirection +1 is to the _left_ when looking from positionEC toward nextEC.

# if POLYLINE_DASH
            // Compute the window coordinates of the points.
            vec4 positionWindow = czm_eyeToWindowCoordinates(positionEC);
            vec4 previousWindow = czm_eyeToWindowCoordinates(prevEC);
            vec4 nextWindow = czm_eyeToWindowCoordinates(nextEC);

            // Determine the relative screen space direction of the line.
            vec2 lineDir;
            if (usePrevious)
            {
                lineDir = normalize(positionWindow.xy - previousWindow.xy);
            }
            else
            {
                lineDir = normalize(nextWindow.xy - positionWindow.xy);
            }
            angle = atan(lineDir.x, lineDir.y) - 1.570796327f; // precomputed atan(1,0)

            // Quantize the angle so it doesn't change rapidly between segments.
            angle = floor(angle / czm_piOverFour + 0.5f) * czm_piOverFour;
#endif

            vec4 clippedPrevWC, clippedPrevEC;
            bool prevSegmentClipped, prevSegmentCulled;
            clipLineSegmentToNearPlane(prevEC.xyz, positionEC.xyz, out clippedPrevWC, out prevSegmentClipped, out prevSegmentCulled, out clippedPrevEC);

            vec4 clippedNextWC, clippedNextEC;
            bool nextSegmentClipped, nextSegmentCulled;
            clipLineSegmentToNearPlane(nextEC.xyz, positionEC.xyz, out clippedNextWC, out nextSegmentClipped, out nextSegmentCulled, out clippedNextEC);

            bool segmentClipped, segmentCulled;
            vec4 clippedPositionWC, clippedPositionEC;
            clipLineSegmentToNearPlane(positionEC.xyz, usePrevious ? prevEC.xyz : nextEC.xyz, out clippedPositionWC, out segmentClipped, out segmentCulled, out clippedPositionEC);

            if (segmentCulled)
            {
                //return vec4(0.0f, 0.0f, 0.0f, 1.0f);
            }

            vec2 directionToPrevWC = normalize(clippedPrevWC.xy - clippedPositionWC.xy);
            vec2 directionToNextWC = normalize(clippedNextWC.xy - clippedPositionWC.xy);

            // If a segment was culled, we can't use the corresponding direction
            // computed above. We should never see both of these be true without
            // `segmentCulled` above also being true.
            if (prevSegmentCulled)
            {
                directionToPrevWC = -1 * directionToNextWC;
            }
            else if (nextSegmentCulled)
            {
                directionToNextWC = -1 * directionToPrevWC;
            }

            vec2 thisSegmentForwardWC, otherSegmentForwardWC;
            if (usePrevious)
            {
                thisSegmentForwardWC = -1 * directionToPrevWC;
                otherSegmentForwardWC = directionToNextWC;
            }
            else
            {
                thisSegmentForwardWC = directionToNextWC;
                otherSegmentForwardWC = -1 * directionToPrevWC;
            }

            vec2 thisSegmentLeftWC = vec2(-thisSegmentForwardWC.y, thisSegmentForwardWC.x);

            vec2 leftWC = thisSegmentLeftWC;
            float expandWidth = width * 0.5f;

            // When lines are split at the anti-meridian, the position may be at the
            // same location as the next or previous position, and we need to handle
            // that to avoid producing NaNs.
            if (!czm_equalsEpsilon(prevEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1) && !czm_equalsEpsilon(nextEC.xyz - positionEC.xyz, vec3(0.0), czm_epsilon1))
            {
                vec2 otherSegmentLeftWC = vec2(-otherSegmentForwardWC.y, otherSegmentForwardWC.x);

                vec2 leftSumWC = thisSegmentLeftWC + otherSegmentLeftWC;
                float leftSumLength = length(leftSumWC);
                leftWC = leftSumLength < czm_epsilon6 ? thisSegmentLeftWC : (leftSumWC / leftSumLength);

                // The sine of the angle between the two vectors is given by the formula
                //         |a x b| = |a||b|sin(theta)
                // which is
                //     float sinAngle = length(cross(vec3(leftWC, 0.0), vec3(-thisSegmentForwardWC, 0.0)));
                // Because the z components of both vectors are zero, the x and y coordinate will be zero.
                // Therefore, the sine of the angle is just the z component of the cross product.
                vec2 u = -1 * thisSegmentForwardWC;
                vec2 v = leftWC;
                float sinAngle = abs(u.x * v.y - u.y * v.x);
                expandWidth = clamp(expandWidth / sinAngle, 0.0f, width * 2.0f);
            }
            //@cp@1
            float expandWidth2 = 20.2f;

            vec2 offset = leftWC * expandDirection * expandWidth * czm_pixelRatio;
            return vec4(clippedPositionWC.xy + offset, -clippedPositionWC.z, 1.0f) * (czm_projection * clippedPositionEC).w;
        }
        public void GetWindowPosition(vec4 position, vec4 prev, vec4 next, float expandDirection, float width, bool usePrevious, out float angle)
        {
            angle=0.0f;
            //眼睛
            vec4 positionEC = czm_translateRelativeToEye(position.xyz, new vec3(0f));
            vec4 prevEC = czm_translateRelativeToEye(prev.xyz, new vec3(0f));
            vec4 nextEC = czm_translateRelativeToEye(next.xyz, new vec3(0f));
            //旋转
            vec4 positionEC2 = czm_modelViewRelativeToEye * positionEC;

            //window
            vec4 positionWC = czm_eyeToWindowCoordinates(positionEC2);
            //到这里就结束了，可以获得屏幕的位置

            vec4 positionWC3 = getPolylineWindowCoordinates(positionEC, prevEC, nextEC, expandDirection, width, usePrevious, out angle);
            var gl_Position = czm_viewportOrthographic * positionWC3;
            gl_Position = gl_Position / gl_Position.w;
            gl_Position.x *= viewport.x;
            gl_Position.y *= viewport.y;
        }
    }

    public class getPolylineWindowCoordinatesECSampleTest
    {
        public static void Test()
        {
            getPolylineWindowCoordinatesECSample sample = new getPolylineWindowCoordinatesECSample();

            sample.GetWindowPosition(
                new vec4(-2295197.0854307045f,5407145.483743707f,2476719.330091352f, 1), 
                new vec4(-2295187.0854307045f, 5407155.483743707f, 2476729.330091352f, 1f),
                new vec4(1243887.88190491f, -5063473.253770297f, 3661184.926798582f, 1f),
                -1,
                5, false, out float angle);      
        }
    }
}
